package openjdk.concurrent;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * CountDownLatch类位于java.util.concurrent包下，它可以实现类似计数器的功能
 * <p>
 * 应用场景：
 * 有一个任务A，它要等待其他4个任务执行完毕之后才能执行，此时就可以利用CountDownLatch来实现这种功能。
 * <p>
 * CountDownLatch 中有一个计数器,可以在构造函数中指定，当计数器不为0时，其 await()方法回让线程等待，
 * 可以调用其countDown()减少器计数器的值，当其计数器的值为0时，await()方法立即返回，让等待的线程得以执行。
 * <p>
 * <p>
 * 示例利用其特性，模拟了100米赛跑，10名选手已经准备就绪，只等裁判一声令下。当所有人都到达终点时，比赛结束。
 */
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {

        // 开始的倒数锁
        final CountDownLatch begin = new CountDownLatch(1);

        // 结束的倒数锁
        final CountDownLatch end = new CountDownLatch(10);

        // 十名选手
        final ExecutorService exec = Executors.newFixedThreadPool(10);

        for (int index = 0; index < 10; index++) {
            final int NO = index + 1;
            Runnable run = new Runnable() {
                public void run() {
                    try {
                        // 如果当前计数为零，则此方法立即返回。
                        // 等待
                        System.out.println(">>> B await " + Thread.currentThread().getId());
                        begin.await();
                        // 可以看到只有B await 输出没有 A await输出
                        // 当begin 的数值为0时，才开始输出 A await
                        System.out.println(">>> A await " + Thread.currentThread().getId());
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println("No." + NO + " arrived");
                    } catch (InterruptedException e) {
                    } finally {
                        // 每个选手到达终点时，end就减一
                        end.countDown();
                    }
                }
            };
            exec.submit(run);
            //System.out.println(">>>> " + index );
        }

        // 主线程sleep 200L 后,每个runnable线程都已执行了B await
        // 若无此控制则可能有部分B await 出现在 Game Start 之后
        Thread.sleep(200L);
        System.out.println("Game Start");
        // begin减一，开始游戏
        begin.countDown();
        System.out.println("PA!!!");
        // 等待end变为0，即所有选手到达终点
        // 此时输出的Game Over 被阻塞
        end.await();
        System.out.println("Game Over");
        exec.shutdown();
    }
}
